﻿
using System;

namespace Test30.Core
{
    public class Array2D<T>
    {
        public Array2D(int width, int height)
        {
            if (width < 0) throw new ArgumentOutOfRangeException($"Width must be greater than zero.");
            if (height < 0) throw new ArgumentOutOfRangeException($"Height must be greater than zero.");

            _width = width;
            _values = new T[width * height];
        }

        public Array2D(Vec size)
            : this(size.X, size.Y)
        {
        }

        public Vec Size => new Vec(Width, Height);

        public Rect Bounds => new Rect(Size);
        public int Width => _width;
        public int Height => _values.Length / _width;

        public T this[Vec pos]
        {
            get => this[pos.X, pos.Y];
            set => this[pos.X, pos.Y] = value;
        }

        public T this[int x, int y]
        {
            get => _values[GetIndex(x, y)];
            set => _values[GetIndex(x, y)] = value;
        }
        public void Fill(T value)
        {
            foreach (Vec pos in new Rect(Size))
            {
                this[pos] = value;
            }
        }
        public void Fill(Func<Vec, T> callback)
        {
            foreach (Vec pos in new Rect(Size))
            {
                this[pos] = callback(pos);
            }
        }

        public void CheckBounds(Vec pos)
        {
            if (pos.X < 0) throw new ArgumentOutOfRangeException($"pos.X");
            if (pos.X >= Width) throw new ArgumentOutOfRangeException($"pos.X");
            if (pos.Y < 0) throw new ArgumentOutOfRangeException($"pos.Y");
            if (pos.Y >= Height) throw new ArgumentOutOfRangeException($"pos.Y");
        }

        private int GetIndex(int x, int y)
        {
            return (y * _width) + x;
        }

        private readonly int _width;
        private readonly T[] _values;

    }
}
